//wifi库
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPClient.h>

//sd模块所需库
#include <SPI.h>
#include <SD.h>

//校准时间所需库
#include <WiFiUdp.h>
#include <NTPClient.h>
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "ntp1.aliyun.com", 28800);  // 设置NTP服务器为中国的NTP服务器，时区偏移量为28800秒（北京时间）

//任意引脚串口通信
#include <SoftwareSerial.h>
SoftwareSerial mySerial(D3, D4);  // 定义软件串口对象，D3为RX引脚，D4为TX引脚

//ws2812灯
#include <Adafruit_NeoPixel.h>
#define PIN D8        // 定义Neopixel数据引脚
#define NUMPIXELS 30  // 定义LED灯珠数量
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
int mode = 0;

ESP8266WebServer esp8266_server(80);  // 建立网络服务器对象，该对象用于响应HTTP请求。监听端口（80）
const char* ssid = "liu-room";        // WiFi名
const char* password = "qhlpptdyfn";  // WiFi密码

// 定义颜色数组
uint32_t colors[7];

const char* serverIP = "192.168.0.102";  // 即将连接服务器网址/IP
const int serverPort = 8080;             // 即将连接服务器端口

void setup() {
  Serial.begin(115200);  // 启动串口通讯
  mySerial.begin(9600);  // 启动和语音芯片的串口通讯
  delay(1500);

  //初始化内存卡
  initSDard();

  //建立WiFi连接
  initWifi();

  //校准时间
  timeClient.begin();

  //初始化服务器
  initHttpServer();

  //初始化Neopixel
  strip.begin();
  strip.show();

  colors[0] = strip.Color(255, 0, 0);    // Red
  colors[1] = strip.Color(255, 127, 0);  // Orange
  colors[2] = strip.Color(255, 255, 0);  // Yellow
  colors[3] = strip.Color(0, 255, 0);    // Green
  colors[4] = strip.Color(0, 255, 255);  // cyan
  colors[5] = strip.Color(0, 0, 255);    // Blue
  colors[6] = strip.Color(143, 0, 255);  // Purple
}



void loop(void) {
  doMySerial();
  esp8266_server.handleClient();
  if (mode == 1) {
    colorWipe(colors[random(0, 7)], 50);  //随机7色跑马灯
  }

  //流星闪烁
  if (mode == 2) {
    meteorShower();
  }

  //彩虹
  if (mode == 3) {
    rainbowCycle(20);  // 调用彩虹循环函数，设置速度为20ms
  }

  // 流水灯
  if (mode == 4) {
    theaterChase(colors[random(0, 7)], 50);  // 调用流水灯函数，设置颜色为随机，速度为50ms
  }

  //随机节奏
  if (mode == 5) {
    randomLights();
  }
}

void doMySerial() {
  // 接收串口数据
  if (mySerial.available()) {
    String receivedData = mySerial.readString();
    Serial.print("Received data: ");
    Serial.println(receivedData);
    // 处理接收到的数据
    // if (receivedData == "openLight") {  //开灯
    //   sendGetRequestTwo("http://192.168.0.182/light?flag=on");
    // }
    // if (receivedData == "closeLight") {  //关灯
    //   // sendGetRequest("/light?flag=off");
    //   sendGetRequestTwo("http://192.168.0.182/light?flag=off");
    // }
    // if (receivedData == "hello") {  //敬礼
    //   sendGetRequest("http://192.168.0.102:8080?param=play0");
    // }
    // if (receivedData == "circle") {  //转圈
    //   sendGetRequest("http://192.168.0.102:8080?param=play1");
    // }
    // if (receivedData == "dance") {  //跳舞
    //   // sendGetRequest("/light?flag=off");
    //   sendGetRequest("http://192.168.0.102:8080?param=play2");
    // }
    //  if (receivedData == "rest") {  //休息
    //   sendGetRequest("http://192.168.0.102:8080?param=play3");
    // }
    if (receivedData == "horse") {  //打开跑马灯
      mode = 1;
    }
    if (receivedData == "off") {  //关闭跑马灯
      closeWs();
    }

    if (receivedData == "time") {  //查询时间
      timeClient.update();
      // Serial.print("Current time in Beijing: ");
      // Serial.println(timeClient.getFormattedTime());
      String str = timeClient.getFormattedTime();
      String hour = str.substring(0, 2);
      // Serial.println(hour);
      String minute = str.substring(3, 5);
      // Serial.println(minute);
      String myStr = "time-hourStart" + hour + "hourEnd-minuteStart" + minute + "minuteEnd";
      mySerial.println(myStr);
      Serial.println(myStr);
    }
  }
}

void initSDard() {
  if (!SD.begin(4)) {  //初始化,设置CS为GPIO4,即D2
    Serial.println("initialization failed!");
    // while (1)
    //   ;
  }
  Serial.println("initialization successful!");
}

void initWifi() {
  //连接wifi
  WiFi.begin(ssid, password);      // 启动网络连接
  Serial.print("Connecting to ");  // 串口监视器输出网络连接信息
  Serial.print(ssid);
  Serial.println(" ...");  // 告知用户NodeMCU正在尝试WiFi连接

  int i = 0;  // 这一段程序语句用于检查WiFi是否连接成功
  Serial.print(i++);
  Serial.print(' ');  // 此处通过While循环让NodeMCU每隔一秒钟检查一次WiFi.status()函数返回值

  // WiFi连接成功后将通过串口监视器输出连接成功信息
  Serial.println('\n');
  Serial.print("Connected to ");
  Serial.println(WiFi.SSID());  // 通过串口监视器输出连接的WiFi名称
  Serial.print("IP address:\t");
  Serial.println(WiFi.localIP());  // 通过串口监视器输出ESP8266-NodeMCU的IP
}

void initHttpServer() {
  //服务器接口
  esp8266_server.on("/light", handleLight);          //灯
  esp8266_server.on("/playMusic", handlePlayMusic);  //语音识别模块
  esp8266_server.on("/wsLight", handleWsLight);      //ws2812灯带

  esp8266_server.onNotFound(handleUserRequest);  // 处理其它网络请求

  // 启动网站服务
  esp8266_server.begin();
  Serial.println("HTTP server started");
}

void handlePlayMusic() {
  String str = esp8266_server.arg("param");
  Serial.println(str);
  mySerial.println(str);
  esp8266_server.send(200, "text/plain", "ok");  //发送网页
}

void handleControl() {
  String str = esp8266_server.arg("param");
  Serial.println(str);
  mySerial.println(str);
  esp8266_server.send(200, "text/plain", "ok");  //发送网页
}

void handleLight() {
  String str = esp8266_server.arg("flag");
  Serial.println(str);
  esp8266_server.send(200, "text/plain", "ok");  //发送网页
}

void handleWsLight() {
  String str = esp8266_server.arg("param");
  Serial.println(str);
  if (str.startsWith("color")) {
    if (str == "color1") {
      mode = 0;
      colorWipe(strip.Color(255, 255, 255), 50);  //设置为白色
    }
    if (str == "color2") {
      mode = 0;
      colorWipe(colors[0], 50);  //设置为红色
    }
    if (str == "color3") {
      mode = 0;
      colorWipe(colors[1], 50);  //设置为橙色
    }
    if (str == "color4") {
      mode = 0;
      colorWipe(colors[2], 50);  //设置为黄色
    }
    if (str == "color5") {
      mode = 0;
      colorWipe(colors[3], 50);  //设置为绿色
    }
    if (str == "color6") {
      mode = 0;
      colorWipe(colors[4], 50);  //设置为青色
    }
    if (str == "color7") {
      mode = 0;
      colorWipe(colors[5], 50);  //设置为蓝色
    }
    if (str == "color8") {
      mode = 0;
      colorWipe(colors[6], 50);  //设置为紫色
    }
  }
  if (str == "random") {
    mode = 5;
  }
  if (str == "theaterChas") {
    mode = 4;
  }
  if (str == "rainbow") {
    mode = 3;
  }
  if (str == "meteor") {
    mode = 2;
  }
  if (str == "horse") {
    mode = 1;
  }
  if (str == "off") {
    closeWs();
  }
  esp8266_server.send(200, "text/plain", "ok");  //发送网页
}

void closeWs() {
  mode = 0;
  // 关闭所有像素
  for (int i = 0; i < strip.numPixels(); i++) {
    strip.setPixelColor(i, 0);  // 将像素颜色设置为黑色
  }
  strip.show();   // 显示关闭的像素
  strip.clear();  // 清除所有像素颜色
}

void colorWipe(uint32_t color, int wait) {
  for (int i = 0; i < strip.numPixels(); i++) {
    strip.setPixelColor(i, color);
    strip.show();
    delay(wait);
  }
}

void meteorShower() {
  static uint8_t hue = 0;
  fadeToBlackBy(20);
  int meteorSize = random(1, 5);
  for (int i = 0; i < meteorSize; i++) {
    int pos = random(NUMPIXELS);
    int brightness = random(50, 255);                // Random brightness value
    strip.setPixelColor(pos, colors[random(0, 7)]);  // Set random brightness
  }
  strip.show();
  delay(50);
  hue++;
}

void fadeToBlackBy(uint8_t fadeValue) {
  for (int i = 0; i < NUMPIXELS; i++) {
    uint32_t color = strip.getPixelColor(i);
    strip.setPixelColor(i, strip.Color(
                             (uint8_t)(((color >> 16) & 0xFF) * fadeValue / 255),
                             (uint8_t)(((color >> 8) & 0xFF) * fadeValue / 255),
                             (uint8_t)((color & 0xFF) * fadeValue / 255)));
  }
}

//彩虹
void rainbowCycle(uint8_t wait) {
  uint16_t i;
  for (i = 0; i < strip.numPixels(); i++) {
    strip.setPixelColor(i, colors[random(0, 7)]);
    strip.show();
    delay(wait);
  }
}

//流水灯
void theaterChase(uint32_t color, int wait) {
  for (int j = 0; j < 5; j++) {  // 重复10次
    for (int q = 0; q < 3; q++) {
      for (int i = 0; i < strip.numPixels(); i = i + 3) {
        strip.setPixelColor(i + q, color);  // 设置像素颜色
      }
      strip.show();
      delay(wait);

      for (int i = 0; i < strip.numPixels(); i = i + 3) {
        strip.setPixelColor(i + q, 0);  // 关闭像素
      }
    }
  }
}

//随机闪
void randomLights() {
  int startPixel = 0;                     // 从第一颗灯开始
  uint32_t color = colors[random(0, 7)];  // 随机颜色
  int numPixels = random(1, 30);          // 随机点亮的灯数量，范围为1到10
  for (int i = startPixel; i < numPixels; i++) {
    strip.setPixelColor(i, color);  // 设置像素颜色
    delay(50);
  }
  strip.show();   // 显示设置的像素颜色
  strip.clear();  // 清除所有像素颜色
}

// 处理用户浏览器的HTTP访问
void handleUserRequest() {

  // 获取用户请求资源(Request Resource）
  String reqResource = esp8266_server.uri();
  Serial.print("reqResource: ");
  Serial.println(reqResource);

  // 通过handleFileRead函数处处理用户请求资源
  bool fileReadOK = handleFileRead(reqResource);

  // 如果在SPIFFS无法找到用户访问的资源，则回复404 (Not Found)
  if (!fileReadOK) {
    esp8266_server.send(404, "text/plain", "404 Not Found");
  }
}

bool handleFileRead(String resource) {  //处理浏览器HTTP访问

  if (resource.endsWith("/")) {  // 如果访问地址以"/"为结尾
    resource = "/index.html";    // 则将访问地址修改为/index.html便于访问
  }

  String contentType = getContentType(resource);  // 获取文件类型

  // Check to see if the file exists:
  if (SD.exists(resource)) {
    File file = SD.open(resource);                 //读模式打开文件
    esp8266_server.streamFile(file, contentType);  // 并且将该文件返回给浏览器
    file.close();
    return true;  // 返回true
  }

  return false;  // 如果文件未找到，则返回false
}

// 获取文件类型
String getContentType(String filename) {
  if (filename.endsWith(".htm")) return "text/html";
  else if (filename.endsWith(".html")) return "text/html";
  else if (filename.endsWith(".css")) return "text/css";
  else if (filename.endsWith(".js")) return "application/javascript";
  else if (filename.endsWith(".png")) return "image/png";
  else if (filename.endsWith(".gif")) return "image/gif";
  else if (filename.endsWith(".jpg")) return "image/jpeg";
  else if (filename.endsWith(".ico")) return "image/x-icon";
  else if (filename.endsWith(".xml")) return "text/xml";
  else if (filename.endsWith(".pdf")) return "application/x-pdf";
  else if (filename.endsWith(".zip")) return "application/x-zip";
  else if (filename.endsWith(".gz")) return "application/x-gzip";
  return "text/plain";
}

void sendGetRequest(String url) {
  if (WiFi.status() == WL_CONNECTED) {
    WiFiClient client;

    // 连接到目标IP和端口
    if (!client.connect(serverIP, serverPort)) {
      Serial.println("Connection failed");
      return;
    }

    // 发送GET请求
    client.print("GET " + url + " HTTP/1.1\r\n");
    client.print("Host: ");
    client.print(serverIP);
    client.print("\r\n");
    client.print("Connection: close\r\n\r\n");

    // 等待响应
    while (client.connected()) {
      if (client.available()) {
        String response = client.readStringUntil('\r');
        Serial.println(response);
      }
    }
    client.stop();
  }
}

String sendGetRequestTwo(String url) {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    Serial.println(url);
    http.begin(url);
    int httpResponseCode = http.GET();
    if (httpResponseCode > 0) {
      String response = http.getString();
      Serial.println(httpResponseCode);
      Serial.println(response);
      return response;
    } else {
      Serial.print("Error on HTTP request: ");
      Serial.println(httpResponseCode);
      return "Error";
    }
    http.end();
  } else {
    return "Error";
  }
}